# -*- coding: utf-8 -*-

import json

import requests
from django.utils.decorators import method_decorator
from django.utils.encoding import smart_unicode
from django.views.decorators.http import require_POST, require_GET
from django.views.generic import TemplateView
from marshmallow import fields

from async.async_job import notify_mch
from common.bankcard import db as bankcard_db
from common.console import db as user_db
from common.mch import db as mch_db
from common.mch import handler as mch_handler
from common.recharge import db as recharge_db
from common.recharge import handler as recharge_handler
from common.recharge.handler import CHARGE_HANDLE
from common.recharge.model import RECHARGE_ORDER_STATUS
from common.risk import db as risk_db
from common.utils import track_logging
from common.utils.api import token_required, check_params, get_client_ip
from common.utils.decorator import response_wrapper, safe_wrapper
from common.utils.exceptions import ParamError, ResourceInsufficient
from common.utils.export import redirect_to_file, gen_filename
from common.utils.respcode import StatusCode
from common.utils.tz import utc_to_local_str
from gis.common.django_ext.decorators import validate_parameters
from gis.common.django_ext.forms import BaseSchema, OptionField

_LOGGER = track_logging.getLogger(__name__)

ORDER_PREFIX = {
    'loki': 'L',
    'witch': 'W',
    'diablo': 'HLG',
    'zszs': 'Z',
    'Durotar': 'D',
    'dagent': 'DAG',
    u'泰坦': 'TT',
    # TODO: ADD ORDER PREFIX HERE
}


class RechargeOrderView(TemplateView):
    def get(self, req):
        query_dct = req.GET.dict()
        if 'status' not in query_dct:
            query_dct['status'] = '{"$neq": "-1"}'

        cond = user_db.get_mchids_filter_by_user(req.user_id)
        if cond and query_dct.get('mch_id') is None:
            query_dct['mch_id'] = cond

        # 老的版本里没有卡名的概念，如果用卡名查询数据会不全
        # 这里临时将卡名转换成卡号查询
        if 'receive_card_name' in query_dct:
            bankcard = bankcard_db.get_bankcard_by_name(query_dct['receive_card_name'])
            if bankcard:
                query_dct['receive_card_num'] = bankcard.account_number
            query_dct.pop('receive_card_name')

        query_dct['pay_account_num'] = '{"$neq": "1"}'
        mch_dct = mch_db.get_mch_dct()
        items, total_count, total_amount = recharge_db.list_order(query_dct)
        resp_items = []
        for item in items:
            data = item.as_dict()
            data['created_at'] = utc_to_local_str(data['created_at'])
            data['updated_at'] = utc_to_local_str(data['updated_at'])
            data['mch_id'] = mch_dct[data['mch_id']]
            data['out_trade_no'] = ORDER_PREFIX.get(data['mch_id'], '') + str(data['out_trade_no'])
            data['id'] = str(data['id'])
            resp_items.append(data)
        return {
            'list': resp_items,
            'page': query_dct.get('$page', 1),
            'total_amount': ('%.2f' % total_amount) if total_amount else '0',
            'size': len(resp_items),
            'total_count': total_count
        }

    @method_decorator(response_wrapper)
    @method_decorator(token_required)
    def dispatch(self, *args, **kwargs):
        return super(RechargeOrderView, self).dispatch(*args, **kwargs)


class SingleRechargeOrderView(TemplateView):
    def get(self, req, order_id):
        order = recharge_db.get_order(int(order_id))
        data = order.as_dict()
        mch_dct = mch_db.get_mch_dct()
        data['mch_id'] = mch_dct[data['mch_id']]
        data['supple_info'] = json.loads(data['context']).get('supple_info', '')
        data['out_trade_no'] = ORDER_PREFIX.get(data['mch_id'], '') + str(data['out_trade_no'])
        data['created_at'] = utc_to_local_str(data['created_at'])
        data['updated_at'] = utc_to_local_str(data['updated_at'])
        return data

    def post(self, req, order_id):
        return self.put(req, order_id)

    def put(self, req, order_id):
        query_dct = json.loads(smart_unicode(req.body))
        recharge_db.upsert_order(query_dct, int(order_id))
        return {}

    def delete(self, req, order_id):
        recharge_db.delete_order(int(order_id))
        _LOGGER.info('delete order: %s, user: %s', order_id, req.user_id)
        return {}

    @method_decorator(response_wrapper)
    @method_decorator(token_required)
    def dispatch(self, *args, **kwargs):
        return super(SingleRechargeOrderView, self).dispatch(*args, **kwargs)


@require_POST
@token_required
@response_wrapper
@safe_wrapper
def approve_recharge_order(req, order_id):
    recharge_order = recharge_db.get_order(order_id)
    data = json.loads(req.body)
    reason = data.get('reason', '')
    if not recharge_order:
        raise ParamError('recharge order_id invalid')
    # if recharge_order.status != RECHARGE_ORDER_STATUS.OVERTIME:
    #     raise ParamError('recharge order_id is not overtime, please wait 5min and try again')
    if recharge_db.add_order_success(order_id, reason):
        recharge_order = recharge_db.get_order(order_id)
        success, _ = notify_mch(order_id)
        ###if success:
        ###recharge_db.update_daily_remain(recharge_order.receive_card_num, recharge_or)
        return {'operation': True, 'notify': success}
    else:
        return {'operation': False, 'notify': False}


@require_POST
@token_required
@response_wrapper
def reject_recharge_order(req, order_id):
    data = json.loads(req.body)
    reason = data['reason']
    recharge_order = recharge_db.get_order(order_id)
    if not recharge_order:
        raise ParamError('recharge order_id invalid')
    # if recharge_order.status != RECHARGE_ORDER_STATUS.OVERTIME:
    #     raise ParamError('recharge order_id is not overtime, please wait 5min and try again')
    if recharge_db.add_order_fail(order_id, reason):
        success, _ = notify_mch(order_id)
        return {'operation': True, 'notify': success}
    else:
        return {'operation': False, 'notify': False}


@require_POST
@token_required
@response_wrapper
def notify_recharge_order(req, order_id):
    recharge_order = recharge_db.get_order(order_id)
    if not recharge_order:
        raise ParamError('recharge order_id invalid')
    success, _ = notify_mch(order_id)
    return {'notify': success}


@require_GET
@token_required
@response_wrapper
def export_recharge_orders(req):
    query_dct = req.GET.dict()

    if 'status' not in query_dct:
        query_dct['status'] = u'{"$notin": [-1]}'
        query_dct['pay_account_num'] = '{"$neq": "1"}'

    # 老的版本里没有卡名的概念，如果用卡名查询数据会不全
    # 这里临时将卡名转换成卡号查询
    if 'receive_card_name' in query_dct:
        bankcard = bankcard_db.get_bankcard_by_name(query_dct['receive_card_name'])
        if bankcard:
            query_dct['receive_card_num'] = bankcard.account_number
        query_dct.pop('receive_card_name')

    filename = gen_filename('recharge')
    header = ['id', 'status_cn', 'mch_id', 'user_id', 'pay_type',
              'pay_account_bank', 'pay_account_num', 'pay_account_username',
              'receive_card_num', 'amount', 'region', 'out_trade_no',
              'created_at', 'updated_at', 'notify_status', 'notify_count', 'order_creater'
              ]
    cn_header = [u'订单id', u'充值状态', u'平台', u'用户ID',
                 u'支付类型', u'支付账号银行', u'支付账号', u'支付用户名',
                 u'收款卡号', u'金额', u'客户地区', u'商户订单号',
                 u'创建订单时间', u'最后修改时间', u'回调状态', u'回调次数', u'操作人'
                 ]
    mch_dct = mch_db.get_mch_dct()
    items, total_count, total_amount = recharge_db.list_order(query_dct)
    resp_items = []
    for item in items:
        data = item.as_dict()
        data['created_at'] = utc_to_local_str(data['created_at'])
        data['updated_at'] = utc_to_local_str(data['updated_at'])
        data['mch_id'] = mch_dct[data['mch_id']]
        data['out_trade_no'] = ORDER_PREFIX.get(data['mch_id'], '') + str(data['out_trade_no'])
        data['id'] = str(data['id'])
        data['status_cn'] = RECHARGE_ORDER_STATUS.get_label(int(data['status'] or 0))
        resp_items.append([data.get(x, '-') for x in header])
    return redirect_to_file(resp_items, cn_header, filename)


@require_POST
@token_required
@response_wrapper
def create_test_order(req):
    query_dct = json.loads(req.body)
    check_params(query_dct, ['mch_id', 'user_id', 'total_fee', \
                             'out_trade_no', 'pay_type', 'pay_account_num', \
                             'pay_account_username', 'bank', 'star'])

    ip = get_client_ip(req)
    resp = recharge_handler.create_test_order(query_dct, ip)
    return resp


def get_mch_order_url(mch_id):
    return mch_db.get_mch_info(int(mch_id)).get('order_url')


def _get_pay_type(pay_type):
    if pay_type == 'bankcard':
        return
    elif pay_type == 'alipay':
        return
    else:
        return ''


def get_out_trade_no(mch_id, user_id, total_fee, pay_type):
    """
    :return:
    {
      "notify_url": "http://103.230.243.13:9001/api/v1/third/unionagency/notify",
      "notify_url": "http://103.230.243.13:9001/api/v1/third/unionagency/notify",
      "account_holder": "刘三儿",
      "context": {
        "user_info": {
          "count_withdraw": 281,
          "user_id": 2568326,
          "account_day": 342,
          "user_name": "",
          "user_alicount": "",
          "count_recharge": 1117,
          "device_ip": "27.204.153.42",
          "bankcard": "",
          "chn": "qm_duia2",
          "active_day": 154,
          "total_withdraw": 228286,
          "charge_success_count": 1117,
          "tel": "18263448915",
          "total_bet": 799086,
          "total_recharge": 284707,
          "name": ""
        }
      },
      "account_num": "6228482342321280237",
      "pay_id": 1676951006850947000,
      "bank": "中国农业银行"
    }
    """
    parameter_dict = {
        'user_id': user_id,
        'pay_amount': total_fee,
        'type': pay_type,
    }
    parameter_dict['sign'] = mch_handler.generate_sign(int(mch_id), parameter_dict)
    url = get_mch_order_url(mch_id)
    headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
    headers[
        'User-Agent'] = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'
    res = requests.post(url, data=json.dumps(parameter_dict),
                        headers=headers, timeout=10).text
    _LOGGER.info('get_out_trade_no res : %s', res)
    j = json.loads(res)
    if int(j['status']) != 0:
        raise ParamError(res)
    return j['data']


class CreateRechargeOrderSchema(BaseSchema):
    mch_id = fields.Integer(required=True)
    user_id = fields.Integer(required=True)
    pay_type = OptionField(options=['bankcard', 'alipay', 'wechat'])
    amount = fields.Float(required=True)
    receive_bankcard_id = fields.Integer(allow_none=True)
    pay_account_username = fields.String(allow_none=True)
    pay_account_num = fields.String(allow_none=True)
    pay_account_bank = fields.String(allow_none=True)


@require_POST
@token_required
@response_wrapper
@validate_parameters(CreateRechargeOrderSchema)
def make_order(req, cleaned_data):
    """
    创建订单逻辑：
    1. 收款银行卡优先使用参数传进来的，如果不传，则自动匹配一个
    2. 支付账号优先使用参数传进来的，如果不传，则使用产品方回回传的
    """

    if cleaned_data['amount'] > 1000000:
        _LOGGER.warning('amount not valid: %s', cleaned_data['amount'])
        raise ParamError('amount invalid')

    user_star = 0
    bankcard = None
    if cleaned_data.get('receive_bankcard_id'):
        user_star = 0
        bankcard = bankcard_db.get_bankcard(cleaned_data['receive_bankcard_id'])
        if bankcard is None:
            raise ParamError(u'银行卡不存在')
        if bankcard.mch_id != cleaned_data['mch_id']:
            raise ParamError(u'该商户不支持此银行卡，请换另外银行卡')
        del cleaned_data['receive_bankcard_id']

    data = get_out_trade_no(cleaned_data['mch_id'], cleaned_data['user_id'], cleaned_data['amount'],
                            cleaned_data['pay_type'])
    # 判断out_trade_no是否存在
    exist_order = recharge_db.get_order_by_out_trade_no(
        cleaned_data['mch_id'], data['pay_id']
    )
    if exist_order:
        raise ParamError('out_trade_no exists')

    data['account_num'] = cleaned_data.get('pay_account_num') or data.get('account_num')
    data['bank'] = cleaned_data.get('pay_account_bank') or data.get('bank')
    data['account_holder'] = cleaned_data.get('pay_account_username') or data.get('account_holder')

    if not data['account_holder']:
        raise ParamError(u'缺少付款账户信息')

    if data['account_num'] and risk_db.get_risky_card_by_num(data['account_num'], cleaned_data['mch_id']):
        _LOGGER.error('black card matched: %s', cleaned_data)
        raise ResourceInsufficient(u'银行卡卡号错误')

    cleaned_data['sdk_version'] = 'Agent_recharge'

    if not bankcard:
        sdk_version = cleaned_data['sdk_version'] = 'Agent_recharge'

        user_star, bankcard = bankcard_db.get_bankcard_by_context(cleaned_data['mch_id'], data['context'],
                                                                  cleaned_data['pay_type'], data['account_num'],
                                                                  sdk_version)

    if not bankcard:
        _LOGGER.error('no bandcard matched mch: %s, data: %s', cleaned_data['mch_id'], data)
        raise ResourceInsufficient(status=StatusCode.NO_VALID_BANKCARD)

    cleaned_data['star'] = user_star
    cleaned_data['receive_card_num'] = bankcard.account_number
    cleaned_data['receive_card_name'] = bankcard.name
    cleaned_data['receive_card_type'] = bankcard.card_type

    cleaned_data['pay_account_username'] = data['account_holder']
    cleaned_data['pay_account_num'] = data['account_num']
    cleaned_data['pay_account_bank'] = data['bank']
    cleaned_data['out_trade_no'] = data['pay_id']

    cleaned_data['context'] = data['context']
    cleaned_data['notify_url'] = data['notify_url']

    cleaned_data['order_creater'] = req.user.username

    order = recharge_db.create_recharge_order(cleaned_data)

    # 尝试将订单提交到同略云处理
    try:
        CHARGE_HANDLE[bankcard.card_type](order, bankcard)
    except Exception, e:
        _LOGGER.error('make_order error: %s', e)

    return {
        "order_id": order.id,
        "amount": '%.2f' % order.amount,
        "bankcard_info": {
            "bank": bankcard.bank,
            "account_number": bankcard.account_number,
            "subbranch": bankcard.subbranch,
            "account_holder": bankcard.account_holder,
        }
    }


@require_POST
@token_required
@response_wrapper
def resend_recharge_order(req, order_id):
    order = recharge_db.resend_order(int(order_id))
    recharge_handler.resend_recharge(order)
    return {'operation': True}
